package demoMod.anm2editor.ui;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import demoMod.anm2editor.fonts.FontKeys;
import demoMod.anm2editor.interfaces.ReloadStringsSubscriber;
import demoMod.anm2editor.localization.LocalizedStrings;
import demoMod.anm2editor.model.KeyFrame;
import demoMod.anm2editor.model.Operation;
import demoMod.anm2editor.model.Project;
import demoMod.anm2editor.model.Track;
import demoMod.gdxform.core.FormManager;
import demoMod.gdxform.enums.GFrameCloseStrategy;
import demoMod.gdxform.enums.GFrameWindowMode;
import demoMod.gdxform.enums.GFrameWindowStyle;
import demoMod.gdxform.helpers.FontHelper;
import demoMod.gdxform.ui.*;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

public class CopyKeyframePropertyWindow extends GFrame implements ReloadStringsSubscriber {
    private final HistoryPanel historyPanel;
    private Track sourceTrack;
    private Track destinationTrack;
    private final int startFrameIndex;
    private int endFrameIndex;
    private final List<BiConsumer<KeyFrame, KeyFrame>> copyPropertiesList = new ArrayList<>();
    private final BiConsumer<KeyFrame, KeyFrame> copyXPositionAction = (src, dst) -> dst.xPosition = src.xPosition;
    private final BiConsumer<KeyFrame, KeyFrame> copyYPositionAction = (src, dst) -> dst.yPosition = src.yPosition;
    private final BiConsumer<KeyFrame, KeyFrame> copyXPivotAction = (src, dst) -> dst.xPivot = src.xPivot;
    private final BiConsumer<KeyFrame, KeyFrame> copyYPivotAction = (src, dst) -> dst.yPivot = src.yPivot;
    private final BiConsumer<KeyFrame, KeyFrame> copyXCropAction = (src, dst) -> dst.xCrop = src.xCrop;
    private final BiConsumer<KeyFrame, KeyFrame> copyYCropAction = (src, dst) -> dst.yCrop = src.yCrop;
    private final BiConsumer<KeyFrame, KeyFrame> copyWidthAction = (src, dst) -> dst.width = src.width;
    private final BiConsumer<KeyFrame, KeyFrame> copyHeightAction = (src, dst) -> dst.height = src.height;
    private final BiConsumer<KeyFrame, KeyFrame> copyXScaleAction = (src, dst) -> dst.xScale = src.xScale;
    private final BiConsumer<KeyFrame, KeyFrame> copyYScaleAction = (src, dst) -> dst.yScale = src.yScale;
    private final BiConsumer<KeyFrame, KeyFrame> copyDelayAction = (src, dst) -> dst.delay = src.delay;
    private final BiConsumer<KeyFrame, KeyFrame> copyVisibleAction = (src, dst) -> dst.visible = src.visible;
    private final BiConsumer<KeyFrame, KeyFrame> copyTintRAction = (src, dst) -> dst.tint.r = src.tint.r;
    private final BiConsumer<KeyFrame, KeyFrame> copyTintGAction = (src, dst) -> dst.tint.g = src.tint.g;
    private final BiConsumer<KeyFrame, KeyFrame> copyTintBAction = (src, dst) -> dst.tint.b = src.tint.b;
    private final BiConsumer<KeyFrame, KeyFrame> copyTintAAction = (src, dst) -> dst.tint.a = src.tint.a;
    private final BiConsumer<KeyFrame, KeyFrame> copyColorOffsetRAction = (src, dst) -> dst.colorOffset.r = src.colorOffset.r;
    private final BiConsumer<KeyFrame, KeyFrame> copyColorOffsetGAction = (src, dst) -> dst.colorOffset.g = src.colorOffset.g;
    private final BiConsumer<KeyFrame, KeyFrame> copyColorOffsetBAction = (src, dst) -> dst.colorOffset.b = src.colorOffset.b;
    private final BiConsumer<KeyFrame, KeyFrame> copyRotationAction = (src, dst) -> dst.rotation = src.rotation;
    private final BiConsumer<KeyFrame, KeyFrame> copyInterpolatedAction = (src, dst) -> dst.interpolated = src.interpolated;

    public CopyKeyframePropertyWindow(BitmapFont bannerFont) {
        super(Gdx.graphics.getWidth() / 2.0F - Gdx.graphics.getWidth() * 0.075F, Gdx.graphics.getHeight() / 2.0F - Gdx.graphics.getHeight() * 0.3F, Gdx.graphics.getWidth() * 0.15F, Gdx.graphics.getHeight() * 0.6F - 45, FontHelper.getFont(FontKeys.SIM_HEI_14), GFrameWindowStyle.CLOSE_BUTTON_ONLY, false);
        setWindowMode(GFrameWindowMode.MODAL);
        Timeline timeline = ((Timeline) FormManager.getInstance().getContainerById(Timeline.ID));
        historyPanel = (HistoryPanel) FormManager.getInstance().getContainerById(HistoryPanel.ID);
        startFrameIndex = timeline.getPointerIndex();
        List<String> strings = LocalizedStrings.getStrings("CopyKeyframePropertyWindow");
        setTitle(strings.get(0));
        setResizable(false);
        setCloseStrategy(GFrameCloseStrategy.EXIT_ON_CLOSE);
        LocalizedStrings.subscribe(this);
        GLabel lPosition = new GLabel(getWidth() * 0.05F, getHeight() * 0.88F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lPosition.setText(strings.get(1));
        lPosition.setId("lPosition");

        GCheckBox cXPosition = new GCheckBox(getWidth() * 0.35F, getHeight() * 0.88F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyXPositionAction);
                } else {
                    copyPropertiesList.remove(copyXPositionAction);
                }
            }
        };
        cXPosition.setId("cXPosition");
        cXPosition.setText("");
        addElement(cXPosition);

        GCheckBox cYPosition = new GCheckBox(getWidth() * 0.55F, getHeight() * 0.88F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyYPositionAction);
                } else {
                    copyPropertiesList.remove(copyYPositionAction);
                }
            }
        };
        cYPosition.setId("cYPosition");
        cYPosition.setText("");
        addElement(cYPosition);

        GLabel lOrigin = new GLabel(getWidth() * 0.05F, getHeight() * 0.8F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lOrigin.setText(strings.get(2));
        lOrigin.setId("lOrigin");

        GCheckBox cXOrigin = new GCheckBox(getWidth() * 0.3F, getHeight() * 0.8F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyXPivotAction);
                } else {
                    copyPropertiesList.remove(copyXPivotAction);
                }
            }
        };
        cXOrigin.setId("cXOrigin");
        cXOrigin.setText("");
        addElement(cXOrigin);

        GCheckBox cYOrigin = new GCheckBox(getWidth() * 0.5F, getHeight() * 0.8F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyYPivotAction);
                } else {
                    copyPropertiesList.remove(copyYPivotAction);
                }
            }
        };
        cYOrigin.setId("cYOrigin");
        cYOrigin.setText("");
        addElement(cYOrigin);

        GLabel lSrc = new GLabel(getWidth() * 0.05F, getHeight() * 0.72F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lSrc.setText(strings.get(3));
        lSrc.setId("lSrc");

        GCheckBox cXSrc = new GCheckBox(getWidth() * 0.3F, getHeight() * 0.72F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyXCropAction);
                } else {
                    copyPropertiesList.remove(copyXCropAction);
                }
            }
        };
        cXSrc.setId("cXSrc");
        cXSrc.setText("");
        addElement(cXSrc);

        GCheckBox cYSrc = new GCheckBox(getWidth() * 0.5F, getHeight() * 0.72F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyYCropAction);
                } else {
                    copyPropertiesList.remove(copyYCropAction);
                }
            }
        };
        cYSrc.setId("cYSrc");
        cYSrc.setText("");
        addElement(cYSrc);

        GLabel lSize = new GLabel(getWidth() * 0.05F, getHeight() * 0.64F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lSize.setText(strings.get(4));
        lSize.setId("lSize");

        GCheckBox cWidth = new GCheckBox(getWidth() * 0.25F, getHeight() * 0.64F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyWidthAction);
                } else {
                    copyPropertiesList.remove(copyWidthAction);
                }
            }
        };
        cWidth.setId("cWidth");
        cWidth.setText("");
        addElement(cWidth);

        GCheckBox cHeight = new GCheckBox(getWidth() * 0.45F, getHeight() * 0.64F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyHeightAction);
                } else {
                    copyPropertiesList.remove(copyHeightAction);
                }
            }
        };
        cHeight.setId("cHeight");
        cHeight.setText("");
        addElement(cHeight);

        GLabel lScale = new GLabel(getWidth() * 0.05F, getHeight() * 0.56F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lScale.setText(strings.get(5));
        lScale.setId("lScale");

        GCheckBox cXScale = new GCheckBox(getWidth() * 0.25F, getHeight() * 0.56F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyXScaleAction);
                } else {
                    copyPropertiesList.remove(copyXScaleAction);
                }
            }
        };
        cXScale.setId("cXScale");
        cXScale.setText("");
        addElement(cXScale);

        GCheckBox cYScale = new GCheckBox(getWidth() * 0.45F, getHeight() * 0.56F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyYScaleAction);
                } else {
                    copyPropertiesList.remove(copyYScaleAction);
                }
            }
        };
        cYScale.setId("cYScale");
        cYScale.setText("");
        addElement(cYScale);

        GLabel lRotation = new GLabel(getWidth() * 0.05F, getHeight() * 0.48F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lRotation.setText(strings.get(7));
        lRotation.setId("lRotation");

        GCheckBox cRotation = new GCheckBox(getWidth() * 0.35F, getHeight() * 0.48F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyRotationAction);
                } else {
                    copyPropertiesList.remove(copyRotationAction);
                }
            }
        };
        cRotation.setId("cRotation");
        cRotation.setText("");
        addElement(cRotation);

        GLabel lVisible = new GLabel(getWidth() * 0.05F, getHeight() * 0.4F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lVisible.setText(strings.get(8));
        lVisible.setId("lVisible");

        GCheckBox cVisible = new GCheckBox(getWidth() * 0.35F, getHeight() * 0.4F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyVisibleAction);
                } else {
                    copyPropertiesList.remove(copyVisibleAction);
                }
            }
        };
        cVisible.setId("cVisible");
        cVisible.setText("");
        addElement(cVisible);

        GLabel lTint = new GLabel(getWidth() * 0.05F, getHeight() * 0.32F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lTint.setText(strings.get(9));
        lTint.setId("lTint");

        GCheckBox cTintR = new GCheckBox(getWidth() * 0.25F, getHeight() * 0.32F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyTintRAction);
                } else {
                    copyPropertiesList.remove(copyTintRAction);
                }
            }
        };
        cTintR.setId("cTintR");
        cTintR.setText("");
        addElement(cTintR);

        GCheckBox cTintG = new GCheckBox(getWidth() * 0.35F, getHeight() * 0.32F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyTintGAction);
                } else {
                    copyPropertiesList.remove(copyTintGAction);
                }
            }
        };
        cTintG.setId("cTintG");
        cTintG.setText("");
        addElement(cTintG);

        GCheckBox cTintB = new GCheckBox(getWidth() * 0.45F, getHeight() * 0.32F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyTintBAction);
                } else {
                    copyPropertiesList.remove(copyTintBAction);
                }
            }
        };
        cTintB.setId("cTintB");
        cTintB.setText("");
        addElement(cTintB);

        GCheckBox cTintA = new GCheckBox(getWidth() * 0.55F, getHeight() * 0.32F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyTintAAction);
                } else {
                    copyPropertiesList.remove(copyTintAAction);
                }
            }
        };
        cTintA.setId("cTintA");
        cTintA.setText("");
        addElement(cTintA);

        GLabel lColorOffset = new GLabel(getWidth() * 0.05F, getHeight() * 0.24F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lColorOffset.setText(strings.get(10));
        lColorOffset.setId("lColorOffset");

        GCheckBox cColorOffsetR = new GCheckBox(getWidth() * 0.45F, getHeight() * 0.24F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyColorOffsetRAction);
                } else {
                    copyPropertiesList.remove(copyColorOffsetRAction);
                }
            }
        };
        cColorOffsetR.setId("cColorOffsetR");
        cColorOffsetR.setText("");
        addElement(cColorOffsetR);

        GCheckBox cColorOffsetG = new GCheckBox(getWidth() * 0.55F, getHeight() * 0.24F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyColorOffsetGAction);
                } else {
                    copyPropertiesList.remove(copyColorOffsetGAction);
                }
            }
        };
        cColorOffsetG.setId("cColorOffsetG");
        cColorOffsetG.setText("");
        addElement(cColorOffsetG);

        GCheckBox cColorOffsetB = new GCheckBox(getWidth() * 0.65F, getHeight() * 0.24F + getHeight() * 0.025F, getWidth() * 0.06F, getWidth() * 0.06F, bannerFont) {
            @Override
            public void onClick() {
                if (getState() == 1) {
                    copyPropertiesList.add(copyColorOffsetBAction);
                } else {
                    copyPropertiesList.remove(copyColorOffsetBAction);
                }
            }
        };
        cColorOffsetB.setId("cColorOffsetB");
        cColorOffsetB.setText("");
        addElement(cColorOffsetB);

        addElement(lPosition);
        addElement(lOrigin);
        addElement(lSrc);
        addElement(lSize);
        addElement(lScale);
        addElement(lRotation);
        addElement(lVisible);
        addElement(lTint);
        addElement(lColorOffset);

        GLabel lSource = new GLabel(getWidth() * 0.05F, getHeight() * 0.16F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lSource.setText(strings.get(11));
        lSource.setId("lSource");
        addElement(lSource);

        GButton btnSource = new GButton(getWidth() * 0.2F, getHeight() * 0.16F + getHeight() * 0.02F, getWidth() * 0.2F, getHeight() * 0.05F, bannerFont) {
            @Override
            public void onClick() {
                List<Track> tracks = new ArrayList<>(Project.currentProject.getTracks());
                if (destinationTrack != null) {
                    tracks.removeIf(track -> track == destinationTrack);
                }
                OptionWindow optionWindow = new OptionWindow(strings.get(19), tracks.stream().map(Track::getName).collect(Collectors.toList()), bannerFont, 0);
                optionWindow.setConfirmCallback(index -> {
                    this.setText(tracks.get(index).getName());
                    sourceTrack = tracks.get(index);
                });
            }
        };
        btnSource.setText("");
        btnSource.setId("btnSource");
        btnSource.setBackground(new Color(0, 47.0F / 255.0F, 147.0F / 255.0F, 1.0F));
        addElement(btnSource);

        GLabel lTarget = new GLabel(getWidth() * 0.45F, getHeight() * 0.16F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lTarget.setText(strings.get(12));
        lTarget.setId("lTarget");
        addElement(lTarget);

        GButton btnTarget = new GButton(getWidth() * 0.75F, getHeight() * 0.16F + getHeight() * 0.02F, getWidth() * 0.2F, getHeight() * 0.05F, bannerFont) {
            @Override
            public void onClick() {
                List<Track> tracks = new ArrayList<>(Project.currentProject.getTracks());
                if (sourceTrack != null) {
                    tracks.removeIf(track -> track == sourceTrack);
                }
                OptionWindow optionWindow = new OptionWindow(strings.get(20), tracks.stream().map(Track::getName).collect(Collectors.toList()), bannerFont, 0);
                optionWindow.setConfirmCallback(index -> {
                    this.setText(tracks.get(index).getName());
                    destinationTrack = tracks.get(index);
                });
            }
        };
        btnTarget.setText("");
        btnTarget.setId("btnTarget");
        btnTarget.setBackground(new Color(0, 47.0F / 255.0F, 147.0F / 255.0F, 1.0F));
        addElement(btnTarget);

        GLabel lLength = new GLabel(getWidth() * 0.05F, getHeight() * 0.08F + getHeight() * 0.035F, getWidth(), getHeight() * 0.07F, bannerFont);
        lLength.setText(strings.get(13));
        lLength.setId("lLength");
        addElement(lLength);

        GTextField tLength = new GTextField(getWidth() * 0.35F, getHeight() * 0.085F, getWidth() * 0.15F, getHeight() * 0.05F, bannerFont) {
            @Override
            public boolean keyDown(int keyCode) {
                if (keyCode == Input.Keys.ENTER && FormManager.getInstance().getEventHooks().getCurrentFocusedElement() == this) {
                    FormManager.getInstance().getEventHooks().setCurrentFocusedElement(getParent());
                    endFrameIndex = startFrameIndex + (int) Double.parseDouble(getText());
                    doCopy();
                    FormManager.getInstance().removeContainer(CopyKeyframePropertyWindow.this);
                    return true;
                }
                return super.keyDown(keyCode);
            }

            @Override
            public void deactivate() {
                if (getText().length() > 0 && getText().length() < 10) {
                    endFrameIndex = startFrameIndex + (int) Double.parseDouble(getText());
                }
            }
        };
        tLength.setId("tLength");
        tLength.setCharFilter("[0-9]");
        addElement(tLength);

        GButton btnConfirm = new GButton(getWidth() * 0.55F - 6.0F, getHeight() * 0.015F, getWidth() * 0.2F, getHeight() * 0.05F, bannerFont) {
            @Override
            public void onClick() {
                if (tLength.getText().length() > 0 && tLength.getText().length() < 10) {
                    endFrameIndex = startFrameIndex + (int) Double.parseDouble(tLength.getText());
                    doCopy();
                    FormManager.getInstance().removeContainer(CopyKeyframePropertyWindow.this);
                } else {
                    new MessageBox(strings.get(16), strings.get(18), FontHelper.getFont(FontKeys.SIM_HEI_14));
                }
            }
        };
        btnConfirm.setText(strings.get(14));
        btnConfirm.setId("btnConfirm");
        btnConfirm.setBackground(new Color(0, 47.0F / 255.0F, 147.0F / 255.0F, 1.0F));
        btnConfirm.setBorderWidth(2);
        btnConfirm.setBorder(Color.LIGHT_GRAY.cpy());
        addElement(btnConfirm);

        GButton btnCancel = new GButton(getWidth() * 0.75F, getHeight() * 0.015F, getWidth() * 0.2F, getHeight() * 0.05F, bannerFont) {
            @Override
            public void onClick() {
                FormManager.getInstance().removeContainer(CopyKeyframePropertyWindow.this);
            }
        };
        btnCancel.setText(strings.get(15));
        btnCancel.setId("btnCancel");
        btnCancel.setBackground(new Color(0, 47.0F / 255.0F, 147.0F / 255.0F, 1.0F));
        btnCancel.setBorderWidth(2);
        btnCancel.setBorder(Color.LIGHT_GRAY.cpy());
        addElement(btnCancel);
    }

    @Override
    public void onReloadStrings() {

    }

    private void doCopy() {
        List<String> strings = LocalizedStrings.getStrings("CopyKeyframePropertyWindow");
        if (sourceTrack != null && destinationTrack != null) {
            List<KeyFrame> dstBackup = new ArrayList<>();
            for (int i=startFrameIndex;i<endFrameIndex;i++) {
                KeyFrame src = sourceTrack.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrameAt(i);
                KeyFrame dst = destinationTrack.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrameAt(i);
                if (src != null && dst != null) {
                    dstBackup.add(dst.makeCopy());
                    copyPropertiesList.forEach(action -> action.accept(src, dst));
                }
            }
            Operation operation = new Operation() {
                @Override
                public void undo() {
                    for (int i=startFrameIndex;i<endFrameIndex;i++) {
                        KeyFrame dst = destinationTrack.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrameAt(i);
                        final int finalI = i;
                        copyPropertiesList.forEach(action -> {
                            if (finalI - startFrameIndex < dstBackup.size()) {
                                action.accept(dstBackup.get(finalI - startFrameIndex), dst);
                            }
                        });
                    }
                }

                @Override
                public void redo() {
                    for (int i=startFrameIndex;i<endFrameIndex;i++) {
                        KeyFrame src = sourceTrack.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrameAt(i);
                        KeyFrame dst = destinationTrack.getContent(Project.currentProject.getCurrentAnimation().getName()).getKeyFrameAt(i);
                        if (src != null && dst != null) {
                            copyPropertiesList.forEach(action -> action.accept(src, dst));
                        }
                    }
                }
            };
            operation.setName(strings.get(0));
            historyPanel.addOperation(operation);
        } else {
            new MessageBox(strings.get(16), strings.get(17), FontHelper.getFont(FontKeys.SIM_HEI_14));
        }
    }
}
